/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package elaborate.tag_analysis.client.applications.delicious;

import elaborate.tag_analysis.client.TagEntry;
import elaborate.tag_analysis.client.applications.DaoFactory;
import elaborate.tag_analysis.client.applications.ResourceHandler;
import elaborate.tag_analysis.client.ui.model.OndemandResourceTreeModel;
import elaborate.tag_analysis.client.ui.model.ResourceTreeNode;
import elaborate.tag_analysis.client.utils.resource_logs.Category;
import elaborate.tag_analysis.client.utils.resource_logs.ResourceLog;
import elaborate.tag_analysis.client.utils.resource_logs.ResourceLogs;
import elaborate.tag_analysis.client.utils.resource_logs.ResourceStatus;
import elaborate.tag_analysis.client.utils.resource_logs.TagLog;
import elaborate.tag_analysis.model.Resource;
import elaborate.tag_analysis.model.TURRelation;
import elaborate.tag_analysis.model.Tag;
import elaborate.tag_analysis.restws.client.ResourceDao;
import elaborate.tag_analysis.restws.client.TURRelationDao;
import elaborate.tag_analysis.restws.client.TagDao;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;

/**
 *
 * @author lendle
 */
public class DeliciousResourceHandler implements ResourceHandler {

    protected ResourceLogs resourceLogs;
    private DaoFactory daoFactory=null;

    public DeliciousResourceHandler(ResourceLogs resourceLogs, DaoFactory daoFactory) {
        this.resourceLogs = resourceLogs;
        this.daoFactory=daoFactory;
    }

    /**
     * Get the value of resourceLogs
     *
     * @return the value of resourceLogs
     */
    public ResourceLogs getResourceLogs() {
        return resourceLogs;
    }

    /**
     * Set the value of resourceLogs
     *
     * @param resourceLogs new value of resourceLogs
     */
    public void setResourceLogs(ResourceLogs resourceLogs) {
        this.resourceLogs = resourceLogs;
    }

    @Override
    public List<String> getCategories() throws Exception {
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(
                new FileInputStream(
                new File("applications\\delicious\\computer.category.selected")),
                "utf-8"));
        List<String> results = new ArrayList<String>();
        String str = reader.readLine();
        while (str != null) {
            results.add(str);

            str = reader.readLine();
        }
        reader.close();
        return results;
    }

    @Override
    public List<Resource> loadResources(String categoryId) throws Exception {
        List<Resource> results = new ArrayList<Resource>();
        if (resourceLogs.getCategory(categoryId) == null) {
            Category category = new Category();
            category.setId(categoryId);
            resourceLogs.getCategories().add(category);
            if (categoryId != null) {
                //load resources by issuing queries against server
                ResourceDao dao = daoFactory.createResourceDao();
                List<Resource> resources = dao.findByParentResourceId("delicious", categoryId);
                //System.out.println("resources=" + resources+":"+resources.size());
                if (resources != null && resources.isEmpty() == false) {
                    for (Resource _resource : resources) {
                        results.add(_resource);
                    }
                } else {
                    //load resources by analyzing the corresponding open directory page
                    try {
                        URL url = new URL(categoryId);
                        URL[] urls = extractWebSitesFromCategory(url);
                        resources = new ArrayList<Resource>();
                        if (urls != null) {
                            for (URL _url : urls) {
                                //System.out.println(_url);
                                Resource _resource = new Resource();
                                _resource.setApplicationId("delicious");
                                _resource.setId(_url.toString());
                                _resource.setName(_url.toString());
                                _resource.setValue(_url.toString());
                                _resource.setParentResourceId(categoryId);
                                resources.add(_resource);
                                results.add(_resource);

                                ResourceLog resourceLog = new ResourceLog();
                                resourceLog.setId(_resource.getId());
                                if (resourceLogs.getResource(categoryId, _resource.getId()) == null) {
                                    resourceLogs.getCategory(categoryId).getResources().add(resourceLog);
                                }
                            }
                        }
                        dao.newResource(resources.toArray(new Resource[0]));
                    } catch (Exception ex) {
                        Logger.getLogger(OndemandResourceTreeModel.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        } else {
            //load resources from logs directly
            for (ResourceLog resourceLog : resourceLogs.getCategory(categoryId).getResources()) {
                Resource _resource = new Resource();
                _resource.setApplicationId("delicious");
                _resource.setId(resourceLog.getId());
                _resource.setName(resourceLog.getId());
                _resource.setValue(resourceLog.getId());
                _resource.setParentResourceId(categoryId);
                results.add(_resource);
            }
        }
        return results;
    }

    private URL[] extractWebSitesFromCategory(URL categoryURL) throws Exception {
        List<URL> urls = new ArrayList<URL>();

        BufferedReader input = new BufferedReader(new InputStreamReader(categoryURL.openStream(), "utf-8"));
        try {
            Document doc = new Tidy().parseDOM(input, null);
            //Document doc=db.parse(new InputSource(input));
            NodeList nodeList = doc.getDocumentElement().getElementsByTagName("ul");
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    Element ulElement = (Element) node;
                    if ("directory-url".equals(ulElement.getAttribute("class"))) {
                        NodeList aNodeList = ulElement.getElementsByTagName("a");
                        for (int j = 0; j < aNodeList.getLength(); j++) {
                            Element aElement = (Element) aNodeList.item(j);
                            String url = aElement.getAttribute("href");
                            //System.out.println(url);
                            urls.add(new URL(url));
                        }
                    }
                }
            }
        } finally {
            input.close();
        }
        return urls.toArray(new URL[0]);
    }

    @Override
    public ResourceStatus getResourceStatus(ResourceTreeNode treeNode) throws Exception {
        if (treeNode.isCategory()) {
            //for category node, this method does not need to be invoked
            return null;
        } else {
            //for non-category resource node
            if (treeNode.getStatus() == ResourceTreeNode.STATUS_UNKNOWN && treeNode.getResource() != null) {
                ResourceLog resourceLog = resourceLogs.getResource(treeNode.getResource().getParentResourceId(), treeNode.getResource().getId());
                if (resourceLog != null && resourceLog.getStatus().equals(ResourceStatus.UNKNOWN) == false) {
                    if (resourceLog.getStatus().equals(ResourceStatus.WITHTAGS)) {
                        return ResourceStatus.WITHTAGS;
                        //treeNode.setStatus(ResourceTreeNode.STATUS_TAGS);
                    } else {
                        return ResourceStatus.NODATA;
                        //treeNode.setStatus(ResourceTreeNode.STATUS_NO_TAGS);
                    }
                } else {
                    TURRelationDao dao = daoFactory.createTURRelationDao();
                    List<TURRelation> list = dao.findByResourceId(treeNode.getResource().getApplicationId(), treeNode.getResource().getId());
                    if (list == null || list.isEmpty()) {
                        //System.out.println("no tag for: "+treeNode.getResource().getId());
                        return ResourceStatus.UNKNOWN;
                        //treeNode.setStatus(ResourceTreeNode.STATUS_UNKNOWN);
                    } else {
                        return ResourceStatus.WITHTAGS;
                    }
                }
            } else {
                return ResourceStatus.UNKNOWN;
            }
        }
    }

    @Override
    public boolean isCategoryStatusVerified(ResourceTreeNode treeNode) throws Exception {
        Category category = resourceLogs.getCategory(treeNode.toString());
        if (category != null && category.getResources().isEmpty() == false) {
            //utilize resourceLog
            for (ResourceLog resourceLog : category.getResources()) {
                if (resourceLog.getStatus().equals(ResourceStatus.UNKNOWN)) {
                    //the status is unknown
                    return false;
                }
            }
        } else {
            if (treeNode.getChildren().isEmpty()) {
                return false;
            } else {
                for (ResourceTreeNode childNode : treeNode.getChildren()) {
                    if (childNode.getStatus() == ResourceTreeNode.STATUS_UNKNOWN) {
                        //the status is unknown
                        return false;
                    }
                }
            }
        }
        //assume the status is known
        return true;
    }

    @Override
    public void saveTags(List<TagEntry> entries, ResourceTreeNode workingNode, ResourceTreeNode categoryNode) throws Exception {
        List<Tag> tags = new ArrayList<Tag>();
        List<TagLog> tagLogs = new ArrayList<TagLog>();
        List<TURRelation> turs = new ArrayList<TURRelation>();
        for (int i = 0; i < entries.size(); i++) {
            String tagStr = (String) entries.get(i).getName();
            Tag tag = new Tag();
            tag.setApplicationId("delicious");
            tag.setId(tagStr);
            tag.setName(tagStr);
            tag.setValue(tagStr);
            tags.add(tag);

            TagLog tagLog = new TagLog();
            tagLog.setId(tagStr);
            tagLogs.add(tagLog);

            TURRelation tur = new TURRelation();
            tur.setApplicationId("delicious");
            //tur.setId(UUID.randomUUID().toString());
            tur.setResourceId(workingNode.getResource().getId());
            tur.setTagId(tagStr);
            tur.setUserId("-1");
            turs.add(tur);
        }
        if (tags.isEmpty() == false) {
            daoFactory.createTagDao().newTag(tags.toArray(new Tag[0]));
            workingNode.setStatus(ResourceTreeNode.STATUS_TAGS);
            resourceLogs.getResource(categoryNode.getResource().getId(), workingNode.getResource().getId()).setStatus(ResourceStatus.WITHTAGS);
        } else {
            workingNode.setStatus(ResourceTreeNode.STATUS_NO_TAGS);
            resourceLogs.getResource(categoryNode.getResource().getId(), workingNode.getResource().getId()).setStatus(ResourceStatus.NODATA);
        }
        if (turs.isEmpty() == false) {
            daoFactory.createTURRelationDao().newTURRelation(turs.toArray(new TURRelation[0]));
        }

        resourceLogs.getResource(categoryNode.getResource().getId(), workingNode.getResource().getId()).getTags().clear();
        resourceLogs.getResource(categoryNode.getResource().getId(), workingNode.getResource().getId()).getTags().addAll(tagLogs);
    }
}
